#!/bin/sh
# rc.cleanup - umount all fs cleanly for shutdown
# (C) James Budiono 2012, 2013
# License: GNU GPL Version 3 or later
#
# version 
# 1.0 - initial release
# 1.1 - heavy re-work
# 2.0 - heavy re-work, now using pivot_root
#

if [ "$1" = "" -a -f /tmp/debugshutdown ] ; then
	exec &>/dev/console ; set -x
fi

### configuration parameters
KILL_DELAY=1.5		# in seconds
OLDROOT=/oldroot
CLEANUP=/cleanup
### static executables
BB=/bin/busybox
CRYPTSETUP=/bin/cryptsetup
#-
LVM=/sbin/lvm
MDADM=/sbin/mdadm

BASELINE_MOUNT=/initrd/files

if [ -f /etc/rc.d/PUPSTATE ] ; then
	. /etc/rc.d/PUPSTATE
	if [ $PUPMODE -eq 2 ] ; then
		exit
	fi
fi

### utilities - kill processes. Will kill everything except ourself and init
# $1 kill flags, everything else - PIDs to kill
kill_processes() {
	killflags=$1
	shift
	for PID in $*; do
		case $PID in
			1|$$) ;; # echo dont kill init or own process 
			*) $BB kill $killflags $PID 2> dev/null ;;
		esac
	done
}

### utilities - get list of all processes, except transport (ntfs-3g, nbd-client)
all_processes_except_transport() {
	$BB ps | $BB sed -e '/ntfs-3g/ d; /nbd-client/ d; /posixovl/ d; s/^ *//; s/ .*//' | $BB sort -rn
}

### utilities - remove and unmount branches from aufs
# need /sys and $OLDROOT
unstack_aufs() {
	# find all the branches of aufs	                   
	AUFS_ROOT_ID=$($BB awk -v oldroot=$OLDROOT '$2==oldroot && $3=="aufs" { match($4,/si=[0-9a-f]*/); print "si_" substr($4,RSTART+3,RLENGTH-3); exit }' /proc/mounts)
	[ -z "$AUFS_ROOT_ID" ] && AUFS_ROOT_ID=$(ls /sys/fs/aufs | head -1)
	BRANCHES=$($BB sed 's/=.*//' /sys/fs/aufs/$AUFS_ROOT_ID/br[0-9]*)
	echo -n $1
	# remove all branches from union and unmount them
	for b in $BRANCHES; do
		#echo mount -t aufs -o remount,del:$b aufs $OLDROOT
		echo "* ${b#${OLDROOT}}"
		$BB mount -t aufs -o remount,del:$b aufs $OLDROOT
		$BB umount -d -r $b
	done
	echo
}

### utilities - mount /proc and /sys 
mount_proc_sys() {
	$BB mount -t proc proc /proc	# need proc to kill & busybox self-exec
	$BB mount -t sysfs sysfs /sys	# need sys to find the branches
}

#============================================
#                  main
#============================================

PATH=/bin:/sbin:/usr/bin:/usr/sbin

[ ! -e /proc/mounts ] && mount_proc_sys

DEBUG=
case $(cat /proc/cmdline) in
	*debuginitrd*)   DEBUG=1 ; set -x   ;;
	*debugshutdown*) DEBUG=1 ; DSHELL=1; set -x   ;;
esac

[ -f /tmp/debugshutdown ] && DEBUG=1 && set -x
[ -f /tmp/shutdownshell ] && DSHELL=1

if [ "$DEBUG" = "1" ] ; then
	echo -e "\n===================${1}===================\n"
fi

### assume /proc and /sys still mounted
case "$1" in
	"") ### phase1 ###
		if [ "$(mount | grep ' /initrd/files ')" = "" ] ; then
			umount.crypto_LUKS all
			umount-FULL -in -a -r -d >/dev/null 2>&1
			exit
		fi
		if [ ! -f /tmp/shutdown_pivot_initrd ] ; then # /sbin/poweroff
			rm -f /tmp/shutdown_pivot_initrd /tmp/debugshutdown /tmp/shutdownshell
		fi
		$BB mount -o remount,rw $BASELINE_MOUNT
		$BB mkdir $BASELINE_MOUNT/$OLDROOT
		$BB cp "$0" $BASELINE_MOUNT/$CLEANUP
		$BB cp /sbin/umount.crypto_LUKS ${BASELINE_MOUNT}/
		#-----------------
		cd $BASELINE_MOUNT
		$BB mkdir -p etc dev proc sys root tmp etc/rc.d
		SH=ash
		[ -e bin/sh ] && SH=sh
		cp /etc/rc.d/PUPSTATE etc/rc.d
		ln -s sys run
		[ -f /tmp/debugshutdown ] && echo > tmp/debugshutdown
		[ -f /tmp/shutdownshell ] && echo > tmp/shutdownshell
		$BB pivot_root . $BASELINE_MOUNT/$OLDROOT
		if [ ! -e dev/loop0 ] ; then
			$BB mount -o move $OLDROOT/dev /dev
			$BB mount -o move $OLDROOT/dev /sys
			$BB mount -o move $OLDROOT/dev /proc
		fi
		#--
		if [ "$DEBUG" ] ; then
			exec $BB chroot . /bin/ash $CLEANUP phase2 <dev/console >dev/console 2>&1
		else
			exec $BB chroot . /bin/ash $CLEANUP phase2 <dev/null >dev/null 2>&1
		fi
	;;
esac

########## phase2 #########

if [ -x /bin/grep -a -z "$(grep '^devtmpfs ' /proc/mounts)" ] ; then
	mount -t devtmpfs devtmpfs /dev 2>/dev/null
fi

# sync and then kill all process except transport
$BB sync
kill_processes "" $(all_processes_except_transport)
$BB sleep $KILL_DELAY
kill_processes -9 $(all_processes_except_transport)
sync

# unmount idle devices
#exec 3> /dev/holdme	# keep /dev mounted r/w - needed by lvm/mdadm
$BB umount -a -r -d

# un-wind our aufs root
if [ "$PUNIONFS" = "aufs" ]; then
	mount_proc_sys
	[ "$DEBUG" ] && lsof
	[ "$DEBUG" ] && sleep 10
	[ "$DEBUG" ] && ps
	[ "$DEBUG" ] && sleep 5
	[ "$DEBUG" ] && mount
	[ "$DEBUG" ] && sleep 10
	# remount ro, disable xino, so r/w layer is unlocked and can be removed
	$BB mount -t aufs -o ro,remount,noxino,noplink aufs $OLDROOT
	unstack_aufs "#1: " # remove aufs branches
	#[ "$DEBUG" ] && sleep 10
	unstack_aufs "#2: " # do it twice to ensure pup_rw is unmounted
	umount -a -r -d		# unmount savefile, savedevice and aufs
fi

if [ "$PUNIONFS" = "overlay" ]; then
	echo -n FIXME
fi

[ "$DEBUG" ] && sleep 10

if [ -x $CRYPTSETUP ] ; then
	mount_proc_sys
	/umount.crypto_LUKS all
	umount -a -r -d
	[ "$DEBUG" ] && sleep 5
fi

if [ -x $LVM ] ; then # remove all lvm devices - note lvm before mdadm
	mount_proc_sys
	$LVM vgchange -an --sysinit
	umount -a -r -d
fi

if [ -x $MDADM ] ; then # remove all mdadm devices
	mount_proc_sys
	$MDADM --stop --scan
	umount -a -r -d
fi

# final unmount attempt: remove everything
mount_proc_sys
[ "$DEBUG" ] && ps
[ "$DEBUG" ] && sleep 5

#----------------------------------------------------
[ "$DSHELL" ] && exec /bin/ash <dev/console >dev/console 2>&1
#----------------------------------------------------

$BB umount -a -r -d
[ "$DEBUG" ] && sleep 10

# That's it!